package ai4games_tests.tests;

/**
City Patrol. <br>
This demonstrates that the world is not limited to the size of
the display area and that the world 'view' can be scaled and 
panned. <br>
Pick the patrol to follow and then zoom in for a better look.
 */
import g4p_controls.G4P;
import g4p_controls.GAlign;
import g4p_controls.GCScheme;
import g4p_controls.GControlMode;
import g4p_controls.GEvent;
import g4p_controls.GLabel;
import g4p_controls.GOption;
import g4p_controls.GSlider;
import g4p_controls.GToggleGroup;
import game2dai.Domain;
import game2dai.World;
import game2dai.entities.Building;
import game2dai.entities.Obstacle;
import game2dai.entities.Vehicle;
import game2dai.entityshapes.ps.BuildingPic;
import game2dai.entityshapes.ps.PersonPic;
import game2dai.graph.Graph;
import game2dai.graph.GraphEdge;
import game2dai.graph.GraphNode;
import game2dai.maths.Vector2D;
import game2dai.utils.StopWatch;

import java.awt.Point;
import java.awt.Rectangle;

import processing.core.PApplet;

public class Demo_001a extends PApplet {


	Domain wd;
	World world;
	Building[] buildings;
	Obstacle[] stalls;
	Vehicle[] tourists, patrolLeader;

	Graph routes;
	GraphNode[] nodes;
	GraphNode dest = null;
	GraphEdge[] edges;

	StopWatch watch;

	int partSize = 200, partOverlap = 80;
	int nbrTourists = 1000;
	int nbrPatrols = 9;
	int followPatrol = -1;
	int displayBorderX = 80, displayBorderY = 80;
	Rectangle displayArea;
	int count = 0;
	float HALF_CITY_SIZE = 4000;
	boolean draggable = true;

	public void setup() {
		size(640, 480);
		cursor(CROSS);
		displayArea = new Rectangle(0, 0, 480, 480);

		world = new World(480, height, partSize, partOverlap);
		world.panPixelX(width/2);
		world.panPixelY(height/2);
		world.scale(0.9f);
		world.noOverlap(true);

		wd = new Domain(-HALF_CITY_SIZE-80, -HALF_CITY_SIZE-80, HALF_CITY_SIZE+80, HALF_CITY_SIZE+80);
		// Get the navigation map
		routes = Graph.makeFromXML(this, "ai4g/d002_graph.xml");
		nodes = routes.getNodeArray();
		edges = routes.getEdgeArray();

		// Create the buildings
		buildings = Building.makeFromXML(this, "ai4g/d002_building.xml");
		int bcol = 0;
		for (Building b : buildings) {
			bcol = color(random(160, 200), random(160, 200), random(160, 200));
			BuildingPic bpic = new BuildingPic(this, bcol, color(0), 4.0f);
			b.renderer(bpic);
			world.add(b);
		}

		// Create all the moving objects - start with the patrol
		// Create the patrols
		patrolLeader = new Vehicle[nbrPatrols];
		for (int i = 0; i < patrolLeader.length; i++) {
			makePatrol(i);
		}

		// Now create the tourists
		tourists = new Vehicle[nbrTourists];
		for (int i = 0; i < tourists.length; i++) {
			tourists[i] = new Vehicle( 
					new Vector2D(random(-HALF_CITY_SIZE, HALF_CITY_SIZE), 
					random(-HALF_CITY_SIZE, HALF_CITY_SIZE)), 
					8, 
					new Vector2D(40 * (random(1.0f) - 0.5f), 40 * (random(1.0f) - 0.5)), 40 + 28 * random(1.0f), 
					new Vector2D(random(1.0f) - 0.5f, random(1.0f) - 0.5f), 
					1, 
					1, 
					700);

			PersonPic apic = new PersonPic(this, 10, color(255, 160, 255), color(145, 64, 47), color(0), 1);
			tourists[i].AP().wanderOn().wanderFactors(80, 20, 12);
			tourists[i].AP().wallAvoidOn().wallAvoidFactors(3, 20, 2.8, true);
			tourists[i].renderer(apic);
			tourists[i].worldDomain(wd);
			world.add(tourists[i]);
		}
		createGUI();
		customGUI();
		frameRate(100);
		watch = new StopWatch(); // last thing to be done in setup
	}

	public void customGUI() {
		lblBuildings.setText("" + world.nbr_buildings);
		lblWalls.setText("" + world.nbr_walls);
		lblPeople.setText("" + world.nbr_movers);
		lblNodes.setText("" + routes.getNbrNodes());
		lblEdges.setText("" + routes.getNbrEdges());
	}

	public void makePatrol(int patrolNumber) {
		// Get start and end nodes
		float x0, x1, y0, y1;
		GraphNode start, dest;
		Vehicle[] troop;
		troop = Vehicle.makeFromXML(this, "ai4g/d002_patrol.xml");
		do {
			x0 = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
			y0 = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
			x1 = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
			y1 = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
		} 
		while (abs (x1-x0) < 500 && abs(y1-y0) < 500);
		start = routes.getNodeNearest(x0, y0, 0);
		dest = routes.getNodeNearest(x1, y1, 0);

		for (int i = 0;  i < troop.length; i++) {
			troop[i].moveBy(start.xf(), start.yf());
			PersonPic apic = new PersonPic(this, 10, color(160, 160, 255), color(160, 144, 70), color(0), 1);
			troop[i].AP().wallAvoidOn().wallAvoidFactors(3, 20, 2.8, true);;	

			if (i == 0) {
				apic.bodyFill(color(100, 100, 255));
				troop[i].AP().pathOn();
				troop[i].AP().arriveWeight(900);
			}
			else {
				troop[i].AP().offsetPursuitOn(troop[0], Vector2D.sub(troop[i].pos(), troop[0].pos()));
				troop[i].AP().offsetPursuitWeight(30);
			}

			troop[i].renderer(apic);
			troop[i].worldDomain(wd);
			world.add(troop[i]);
		}
		troop[0].AP().pathAddToRoute(routes, dest);
		patrolLeader[patrolNumber] = troop[0];
	}

	public void draw() {
		float deltaTime = (float) watch.getElapsedTime();
		updatePatrolRoutes();
		if (followPatrol >= 0) {
			Point p = world.world2pixel(patrolLeader[followPatrol].pos(), null);
			int dx = 0, dy = 0;
			if (p.x < displayBorderX)
				dx = displayBorderX - p.x;
			else if (p.x > displayArea.width - displayBorderX)
				dx = displayArea.width - displayBorderX - p.x;		
			if (p.y < displayBorderY)
				dy = displayBorderY - p.y;
			else if (p.y > height - displayBorderY)
				dy = height - displayBorderY - p.y;
			world.panPixelXY(dx, dy);
		}
		background(250);
		pushMatrix();
		translate((float)world.xOffset(), (float)(world.yOffset()));
		scale((float) world.scale());
		world.update(deltaTime);
		world.draw();
		popMatrix();
		fill(255, 220, 255);
		noStroke();
		rect(displayArea.width, 0, width - displayArea.width, height);
		if (count++ % 60 == 0) {
			lblFramerate.setText(""+frameRate);
			lblUpdateTime.setText("" + (float)world.worldUpdateTime);
		}
	}

	public void watchPatrol(int fp) {
		followPatrol = fp;
		if (followPatrol >= 0 && followPatrol < patrolLeader.length) {
			Vector2D pos = patrolLeader[followPatrol].pos();
			world.moveToWorldXY(pos.x, pos.y);
		}
	}

	public void updatePatrolRoutes() {
		GraphNode dest;
		for (int i = 0; i < patrolLeader.length; i++) {
			if (patrolLeader[i].AP().pathRoute().size() < 3) {
				float x = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
				float y = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
				dest = routes.getNodeNearest(x, y, 0);
				patrolLeader[i].AP().pathAddToRoute(routes, dest);
			}
		}
	}

	public void mousePressed() {
		draggable = (mouseX >= displayArea.x 
				&& mouseY >= displayArea.y 
				&& mouseX < displayArea.x + displayArea.width 
				&& mouseY <= displayArea.y + displayArea.height);
	}

	public void mouseReleased() {
		draggable = false;
	}

	public void mouseDragged() {
		if (draggable && followPatrol < 0)
			world.panPixelXY(mouseX - pmouseX, mouseY - pmouseY);
	}


	/* =========================================================
	 * ====                   WARNING                        ===
	 * =========================================================
	 * The code in this tab has been generated from the GUI form
	 * designer and care should be taken when editing this file.
	 * Only add/edit code inside the event handlers i.e. only
	 * use lines between the matching comment tags. e.g.

	 void myBtnEvents(GButton button) { //_CODE_:button1:12356:
	     // It is safe to enter your event code here  
	 } //_CODE_:button1:12356:

	 * Do not rename this tab!
	 * =========================================================
	 */

	public void sdrScale_change(GSlider source, GEvent event) { //_CODE_:sdrScale:786162:
		world.scale(source.getValueF());
	} //_CODE_:sdrScale:786162:

	public void option1_clicked1(GOption source, GEvent event) { //_CODE_:option1:504384:
		watchPatrol(0);
	} //_CODE_:option1:504384:

	public void option2_clicked1(GOption source, GEvent event) { //_CODE_:option2:556038:
		watchPatrol(1);
	} //_CODE_:option2:556038:

	public void option3_clicked1(GOption source, GEvent event) { //_CODE_:option3:503628:
		watchPatrol(2);
	} //_CODE_:option3:503628:

	public void option4_clicked1(GOption source, GEvent event) { //_CODE_:option4:531075:
		watchPatrol(3);
	} //_CODE_:option4:531075:

	public void option5_clicked1(GOption source, GEvent event) { //_CODE_:option5:629051:
		watchPatrol(4);
	} //_CODE_:option5:629051:

	public void option6_clicked1(GOption source, GEvent event) { //_CODE_:option6:288050:
		watchPatrol(5);
	} //_CODE_:option6:288050:

	public void option7_clicked1(GOption source, GEvent event) { //_CODE_:option7:545917:
		watchPatrol(6);
	} //_CODE_:option7:545917:

	public void option8_clicked1(GOption source, GEvent event) { //_CODE_:option8:854451:
		watchPatrol(7);
	} //_CODE_:option8:854451:

	public void option9_clicked1(GOption source, GEvent event) { //_CODE_:option9:936626:
		watchPatrol(8);
	} //_CODE_:option9:936626:

	public void option10_clicked1(GOption source, GEvent event) { //_CODE_:option10:581130:
		watchPatrol(-1);
	} //_CODE_:option10:581130:



	// Create all the GUI controls. 
	// autogenerated do not edit
	public void createGUI(){
		G4P.messagesEnabled(false);
		G4P.setGlobalColorScheme(GCScheme.PURPLE_SCHEME);
		G4P.setCursor(ARROW);
		if(frame != null)
			frame.setTitle("Sketch Window");
		sdrScale = new GSlider(this, 510, 10, 460, 20, 10);
		sdrScale.setRotation(PI/2, GControlMode.CORNER);
		sdrScale.setLimits(0.9f, 0.1f, 1.8f);
		sdrScale.setEasing(20);
		sdrScale.setNumberFormat(G4P.DECIMAL, 2);
		sdrScale.setOpaque(false);
		sdrScale.addEventHandler(this, "sdrScale_change");
		label1 = new GLabel(this, 520, 20, 110, 20);
		label1.setText("Patrol To Follow");
		label1.setTextBold();
		label1.setOpaque(false);
		togGroup1 = new GToggleGroup();
		option1 = new GOption(this, 520, 40, 40, 20);
		option1.setText("1");
		option1.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		option1.setOpaque(false);
		option1.addEventHandler(this, "option1_clicked1");
		option2 = new GOption(this, 560, 40, 40, 20);
		option2.setText("2");
		option2.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		option2.setOpaque(false);
		option2.addEventHandler(this, "option2_clicked1");
		option3 = new GOption(this, 600, 40, 40, 20);
		option3.setText("3");
		option3.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		option3.setOpaque(false);
		option3.addEventHandler(this, "option3_clicked1");
		option4 = new GOption(this, 520, 60, 40, 20);
		option4.setText("4");
		option4.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		option4.setOpaque(false);
		option4.addEventHandler(this, "option4_clicked1");
		option5 = new GOption(this, 560, 60, 40, 20);
		option5.setText("5");
		option5.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		option5.setOpaque(false);
		option5.addEventHandler(this, "option5_clicked1");
		option6 = new GOption(this, 600, 60, 40, 20);
		option6.setText("6");
		option6.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		option6.setOpaque(false);
		option6.addEventHandler(this, "option6_clicked1");
		option7 = new GOption(this, 520, 80, 40, 20);
		option7.setText("7");
		option7.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		option7.setOpaque(false);
		option7.addEventHandler(this, "option7_clicked1");
		option8 = new GOption(this, 560, 80, 40, 20);
		option8.setText("8");
		option8.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		option8.setOpaque(false);
		option8.addEventHandler(this, "option8_clicked1");
		option9 = new GOption(this, 600, 80, 40, 20);
		option9.setText("9");
		option9.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		option9.setOpaque(false);
		option9.addEventHandler(this, "option9_clicked1");
		option10 = new GOption(this, 540, 100, 80, 20);
		option10.setText("None");
		option10.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		option10.setOpaque(false);
		option10.addEventHandler(this, "option10_clicked1");
		togGroup1.addControl(option1);
		togGroup1.addControl(option2);
		togGroup1.addControl(option3);
		togGroup1.addControl(option4);
		togGroup1.addControl(option5);
		togGroup1.addControl(option6);
		togGroup1.addControl(option7);
		togGroup1.addControl(option8);
		togGroup1.addControl(option9);
		togGroup1.addControl(option10);
		option10.setSelected(true);
		label2 = new GLabel(this, 520, 150, 120, 20);
		label2.setText("Frame Rate (fps)");
		label2.setTextBold();
		label2.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		label2.setOpaque(false);
		lblFramerate = new GLabel(this, 520, 170, 110, 20);
		lblFramerate.setText("My label");
		lblFramerate.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		lblFramerate.setOpaque(false);
		label3 = new GLabel(this, 520, 220, 120, 20);
		label3.setText("World update time");
		label3.setTextBold();
		label3.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		label3.setOpaque(false);
		lblUpdateTime = new GLabel(this, 520, 240, 110, 20);
		lblUpdateTime.setText("My label");
		lblUpdateTime.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		lblUpdateTime.setOpaque(false);
		label4 = new GLabel(this, 520, 310, 60, 20);
		label4.setText("Buildings");
		label4.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		label4.setOpaque(false);
		lblBuildings = new GLabel(this, 580, 310, 50, 20);
		lblBuildings.setText("???");
		lblBuildings.setTextAlign(GAlign.RIGHT, GAlign.MIDDLE);
		lblBuildings.setOpaque(false);
		label6 = new GLabel(this, 520, 290, 110, 20);
		label6.setText("World Statistics");
		label6.setTextBold();
		label6.setOpaque(false);
		label7 = new GLabel(this, 520, 330, 60, 20);
		label7.setText("Walls");
		label7.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		label7.setOpaque(false);
		lblWalls = new GLabel(this, 580, 330, 50, 20);
		lblWalls.setText("???");
		lblWalls.setTextAlign(GAlign.RIGHT, GAlign.MIDDLE);
		lblWalls.setOpaque(false);
		label9 = new GLabel(this, 520, 350, 60, 20);
		label9.setText("People");
		label9.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		label9.setOpaque(false);
		lblPeople = new GLabel(this, 580, 350, 50, 20);
		lblPeople.setText("???");
		lblPeople.setTextAlign(GAlign.RIGHT, GAlign.MIDDLE);
		lblPeople.setOpaque(false);
		label5 = new GLabel(this, 520, 390, 110, 20);
		label5.setText("Navigation Graph");
		label5.setTextBold();
		label5.setOpaque(false);
		label8 = new GLabel(this, 520, 410, 60, 20);
		label8.setText("Nodes");
		label8.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		label8.setOpaque(false);
		lblNodes = new GLabel(this, 580, 410, 50, 20);
		lblNodes.setText("???");
		lblNodes.setTextAlign(GAlign.RIGHT, GAlign.MIDDLE);
		lblNodes.setOpaque(false);
		label11 = new GLabel(this, 520, 430, 60, 20);
		label11.setText("Edges");
		label11.setTextAlign(GAlign.LEFT, GAlign.MIDDLE);
		label11.setOpaque(false);
		lblEdges = new GLabel(this, 580, 430, 50, 20);
		lblEdges.setText("???");
		lblEdges.setTextAlign(GAlign.RIGHT, GAlign.MIDDLE);
		lblEdges.setOpaque(false);
	}

	// Variable declarations 
	// autogenerated do not edit
	GSlider sdrScale; 
	GLabel label1; 
	GToggleGroup togGroup1; 
	GOption option1; 
	GOption option2; 
	GOption option3; 
	GOption option4; 
	GOption option5; 
	GOption option6; 
	GOption option7; 
	GOption option8; 
	GOption option9; 
	GOption option10; 
	GLabel label2; 
	GLabel lblFramerate; 
	GLabel label3; 
	GLabel lblUpdateTime; 
	GLabel label4; 
	GLabel lblBuildings; 
	GLabel label6; 
	GLabel label7; 
	GLabel lblWalls; 
	GLabel label9; 
	GLabel lblPeople; 
	GLabel label5; 
	GLabel label8; 
	GLabel lblNodes; 
	GLabel label11; 
	GLabel lblEdges; 



}
